﻿/*
 * FileName: Match.cs
 * Module: Core
 * Author: Gourav Das
 * Created: 04/2011
 * Modifications:
 * Copyright: Gourav Das, 2011
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using _22yards.IndependentModules;

namespace _22yards.Core
{
    public class OversBowled
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="OversBowled"/> class.
        /// </summary>
        public OversBowled()
        {
            overs = 0;
            legalballs = 0;
        }
        private short overs;
        private short legalballs;

        /// <summary>
        /// Gets the number of overs bowled so far.
        /// </summary>
        public short Overs 
        {
            get
            {
                return overs;
            }
        }

        /// <summary>
        /// Gets the number of balls bowled in current over.
        /// </summary>
        public short LegalBalls 
        { 
            get
            { 
                return legalballs; 
            } 
        }

        /// <summary>
        /// Increments ball count and if required over count
        /// </summary>
        public void NextBall(CBall ball)
        {
            if (ball.TypeOfExtra == 0)
            {
                ++legalballs;
                if (legalballs == 6)
                {
                    overs++;
                    legalballs = 0;
                }
            }
        }

        /// <summary>
        /// Gets the overs and balls bowled.
        /// </summary>
        /// <returns>string</returns>
        public string GetOversNBallsBowled()
        {
            return Overs.ToString() + "." + LegalBalls.ToString();
        }

    }

    public class Score
    {
        private List<CBall> ballsBowledInCurrentInnings;
        public List<CBall> BallsBowledInCurrentInnings
        {
            get
            {
                return ballsBowledInCurrentInnings;
            }
        }

        /// <summary>
        /// Team name
        /// </summary>
        public string TeamName;

        /// <summary>
        /// Gets or sets the runs scored
        /// </summary>
        /// <value>
        /// The runs.
        /// </value>
        public int Runs { get; set; }

        /// <summary>
        /// Gets or sets the wickets fallen.
        /// </summary>
        /// <value>
        /// The wickets.
        /// </value>
        public int Wickets { get; set; }

        /// <summary>
        /// Gets or sets the oversbowled class.
        /// </summary>
        /// <value>
        /// The oversbowled.
        /// </value>
        public OversBowled Oversbowled { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether [innings in progress].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [innings in progress]; otherwise, <c>false</c>.
        /// </value>
        public bool InningsInProgress { get; set;}
        public int Day { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Score"/> class.
        /// </summary>
        public Score()
        {
            ballsBowledInCurrentInnings = new List<CBall>();
            Runs = 0;
            Wickets = 0;
            Oversbowled = new OversBowled();
            InningsInProgress = false;
            Day = 1;
        }

        /// <summary>
        /// Gets the score in string
        /// </summary>
        /// <param name="AussieFormat">if set to <c>true</c> then return in Aussie format.</param>
        /// <returns></returns>
        public string getScore(bool AussieFormat=false)
        {
            if (AussieFormat)
                return Wickets.ToString() + "\\" + Runs.ToString();
            else
                return Runs.ToString() + "\\" + Wickets.ToString();
        }

        /// <summary>
        /// Returns Legals the balls bowled by bowler in current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short LegalBallsBowledByBowlerCurrentInnings(int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BowlerID == _playerID && c.TypeOfExtra == 0));
        }

        /// <summary>
        /// Totals the balls bowled by bowler in current innings including extras.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short TotalBallsBowledByBowlerCurrentInnings(int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BowlerID == _playerID));
        }


        /// <summary>
        /// Returns the number of extras bowled by bowler current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short TotalExtrasBowledByBowlerCurrentInnings (int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BowlerID == _playerID && c.TypeOfExtra !=0));
        }

        /// <summary>
        /// Returns the runs conceeded off bat by bowler current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short RunsConceededByBowlerCurrentInnings(int _playerID)
        {
            var RunsConceeded = (ballsBowledInCurrentInnings.Where(c => c.BowlerID == _playerID && c.RunsScoredOfBat>0 ));
            short runs = 0;
            foreach (CBall ball in RunsConceeded)
            {
                runs += ball.RunsScoredOfBat;
            }

            return runs;
        }

        /// <summary>
        /// Returns the Sixes conceeded by bowler current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short SixesConceededByBowlerCurrentInnings(int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BowlerID == _playerID && c.RunsScoredOfBat==6));
        }

        /// <summary>
        /// Returns the Fours conceeded by bowler current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short FoursConceededByBowlerCurrentInnings (int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BowlerID == _playerID && c.RunsScoredOfBat ==4));
        }

        /// <summary>
        /// Wickets the taken by bowler current innings. This accounts for stumpings off wide balls
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short WicketsTakenByBowlerCurrentInnings (int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BowlerID == _playerID && c.GotWicket == true && (c.TypeOfExtra==0 || c.TypeOfExtra ==1) && (c.OutStyle ==1 || c.OutStyle ==2 || c.OutStyle==3 || c.OutStyle==4 || c.OutStyle==5)));
        }

        /// <summary>
        /// Balls faced by batsman in current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short BallsFacedByBatsmanCurrentInnings(int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BatsmanID == _playerID && (c.TypeOfExtra == 0 || c.TypeOfExtra == 2 || c.TypeOfExtra == 3 || c.TypeOfExtra == 4 || c.TypeOfExtra == 5 || c.TypeOfExtra == 6)));
        
        }

        /// <summary>
        /// Runs scored by batsman in current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short RunsScoredByBatsmanCurrentInnings(int _playerID)
        {
            var RunsScored = (ballsBowledInCurrentInnings.Where(c => c.BatsmanID == _playerID && c.RunsScoredOfBat>0 && (c.TypeOfExtra == 0 || c.TypeOfExtra==2 || c.TypeOfExtra ==4)));
            short runs = 0;
            foreach (CBall ball in RunsScored)
            {
                runs += ball.RunsScoredOfBat;
            }

            return runs;
        }

        /// <summary>
        /// Boundaries scored by batsman in current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short BoundariesScoredByBatsmanCurrentInnings(int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BatsmanID == _playerID && c.RunsScoredOfBat == 4));    
        }

        /// <summary>
        /// Sixes scored by batsman in current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short SixesScoredByBatsmanCurrentInnings(int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BatsmanID == _playerID && c.RunsScoredOfBat == 6));
        }

        /// <summary>
        /// Dots by batsman in current innings.
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short DotsByBatsmanCurrentInnings(int _playerID)
        {
            return Convert.ToInt16(ballsBowledInCurrentInnings.Count(c => c.BatsmanID == _playerID && c.RunsScoredOfBat == 0 && c.RunsScoredBecauseExtra == 0));
        }

        /// <summary>
        /// Runs conceeded by bowler in his previous ball. Mainly used alongwith Volatlity
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns></returns>
        public short RunConceededBowlerPreviousBall(int _playerID)
        {
            CBall tmpBall = ballsBowledInCurrentInnings.Last(c => c.BatsmanID == _playerID);

            return Convert.ToInt16(tmpBall.RunsScoredBecauseExtra + tmpBall.RunsScoredOfBat);
        }

        /// <summary>
        /// Wickets fallen previous ball. Mainly used with Volatility
        /// </summary>
        /// <param name="_playerID">The _player ID.</param>
        /// <returns>True or False</returns>
        public bool WicketFallenPreviousBall(int _playerID)
        {
            return (ballsBowledInCurrentInnings.Last(c => c.BatsmanID == _playerID)).GotWicket;
        }

    }

    /// <summary>
    /// Class that holds score information inningswise for whole match. So this needs 
    /// innings information because it can be Init
    /// </summary>
    public class MatchScore
    {
        public int NumInnings=0;
        public int GetInningsInProgress()
        {
            for(int i=1;i<=scoreList.Count;i++)
            {
                if (scoreList[i-1].InningsInProgress)
                    return i;
            }
            return -1;
        }

        public MatchScore()
        {
            scoreList = new List<Score>();
        }

        /// <summary>
        /// Inits the specified Matchscore using innings info got from Rules
        /// </summary>
        /// <param name="innings">The innings.</param>
        public void Init (int innings)
        {
            if (scoreList.Capacity > 0)
                scoreList.Clear();
            scoreList.Capacity = innings;
            for (int i = 0; i < innings; i++)
                scoreList.Add(new Score());
            NumInnings = innings;
            scoreList[0].InningsInProgress = true;
        }

        private List<Score> scoreList = new List<Score>();

        /// <summary>
        /// Gets the nth innings score.
        /// </summary>
        /// <param name="innings">The innings.</param>
        /// <returns></returns>
        public Score NthInningsScore(int innings)
        {
            if (innings <= scoreList.Count)
                return scoreList[innings-1];
            else
                return null;
        }

        /// <summary>
        /// Currents the current innings score.
        /// </summary>
        /// <returns></returns>
        public Score CurrentInningsScore()
        {
            return scoreList[GetInningsInProgress() - 1];
        }

        /// <summary>
        /// Gets the Nth innings score value in string.
        /// </summary>
        /// <param name="innings">The innings.</param>
        /// <param name="AussieFormat">if set to <c>true</c> [aussie format].</param>
        /// <returns></returns>
        public string NthInningsScoreValue(int innings, bool AussieFormat)
        {
            if (innings <= scoreList.Count)
                return scoreList[innings - 1].getScore(AussieFormat);
            else
                return null;
        }

        /// <summary>
        /// Currents the innings score value in string.
        /// </summary>
        /// <param name="AussieFormat">if set to <c>true</c> [aussie format].</param>
        /// <returns></returns>
        public string CurrentInningsScoreValue(bool AussieFormat)
        {
            return scoreList[GetInningsInProgress()  - 1].getScore(AussieFormat);
        }

    }

    public class Match
    {
        /// <summary>
        /// Team1 participating in match
        /// </summary>
        public Team Team1;
        /// <summary>
        /// Team2 participating in match
        /// </summary>
        public Team Team2;

        /// <summary>
        /// Gets or sets the rule class.
        /// </summary>
        /// <value>
        /// The match rule.
        /// </value>
        public Rules MatchRule { get; set;}

        /// <summary>
        /// Gets or sets the matchscore structure.
        /// </summary>
        /// <value>
        /// The match score.
        /// </value>
        public MatchScore matchScore { get; set;}

        /// <summary>
        /// Gets the match title.
        /// </summary>
        public string MatchTitle { get; private set; }

        /// <summary>
        /// Gets or sets the state of the match.
        /// </summary>
        /// <value>
        /// The state of the match. 0 - Not started, 1 - InProgress, 2 - over no result, 3 over - result
        /// </value>
        public short matchState { get; set; } //0 - Not started, 1 - InProgress, 2 - Over

        public bool TeamsDecided { get; set; }

        /// <summary>
        /// Gets or sets the type of match.
        /// </summary>
        /// <value>
        /// MatchType enum.
        /// </value>
        public short TypeOfMatch { get; set; } //To be set in MatchSelectionScreen

        public string MatchInGroup { get; set; }

        public short RelativeMatchDay { get; set; }

        /// <summary>
        /// Gets or sets the match time when match starts.
        /// </summary>
        /// <value>
        /// The match time.
        /// </value>
        public short MatchTime { get; set; }

        /// <summary>
        /// Gets or sets the stadium ID.
        /// </summary>
        /// <value>
        /// ID.
        /// </value>
        public int StadiumID { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Match"/> class.
        /// </summary>
        public Match ()
        {
            Team1 = new Team();
            Team2 = new Team();

            if (!Team1.initTeam() || !Team2.initTeam())
            {
                Team1 = null;
                Team2 = null;
            }

            matchScore = new MatchScore();
            TeamsDecided = false;
        }

        /// <summary>
        /// Initializes the specific Match. This is to be done only ater Rules are loaded
        /// </summary>
        /// <param name="team1">The team1 playing the match.</param>
        /// <param name="team2">The team2 playing the match.</param>
        /// <param name="innings">The number of innings to be played.</param>
        public void Init(Team team1,Team team2,int innings)
        {
            MatchTitle = team1.TeamName + " vs " + team2.TeamName;
            Team1 = team1;
            Team2 = team2;
            matchScore.Init(innings);
            TeamsDecided = true;
        }


        public void SaveMatch()
        {
            throw new NotImplementedException();
        }

        public void LoadMatch()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the current bowler bowling.
        /// </summary>
        /// <param name="Bowlers">The bowlers.</param>
        public void GetCurrentBowlers(ref List<cPlayer> Bowlers)
        {
            if (Team1.CurrentTeamBatting)
            {
                Bowlers.Add(Team2.players.First(c => c.IsBowlingCurrently == true));
                Bowlers.Add(Team2.players.First(c => c.IsSetToBowlNext == true));
                if (Bowlers[0] == null || Bowlers[1] == null) 
                    Bowlers=null;
            }
            else if (Team2.CurrentTeamBatting)
            {
                Bowlers.Add(Team1.players.First(c => c.IsBowlingCurrently == true));
                Bowlers.Add(Team1.players.First(c => c.IsSetToBowlNext == true));
                if (Bowlers[0] == null || Bowlers[1] == null)
                    Bowlers = null;
            }
        }

        /// <summary>
        /// Sets the next player to bowl.
        /// </summary>
        /// <param name="cpl">The Player.</param>
        /// <returns></returns>
        public bool SetNextPlayer(ref cPlayer cpl)
        {
            if (!cpl.IsBowlingCurrently)
            {
                cpl.IsSetToBowlNext = true;
                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// Gets the batsmen on crease.
        /// </summary>
        /// <param name="Batsmen">List player.</param>
        public void GetBatsmenOnCrease(ref List<cPlayer> Batsmen)
        {
            if (Team1.CurrentTeamBatting)
            {
                foreach (cPlayer cpl in Team1.players)
                {
                    if (cpl.IsBatting)
                        Batsmen.Add(cpl);
                }
                return;
            }
            else if (Team2.CurrentTeamBatting)
            {
                foreach (cPlayer cpl in Team2.players)
                {
                    if (cpl.IsBatting)
                        Batsmen.Add(cpl);
                }
                return;
            }

            Batsmen = null;
            return;
        }
    }
}
